/**
 * Copyright (C) 2010 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.gjc.client.callback;

import java.util.logging.Logger;

import org.gjc.client.action.Action;

import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.RequestException;
import com.google.gwt.http.client.Response;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;

/**
 * <p>
 * The RequestCallbackRetry has two main purposes:
 * <ul>
 * <li>1. Retry => to make the client more stable due to flaky connections.</li>
 * <li>2. Security => Checks if a user is logged out and automatically redirect
 * to login page if user is logged out.</li>
 * </ul>
 * </p>
 *
 * @author <a href="mailto:mail@raphaelbauer.com">rEyez</<a>
 *
 * @param <R>
 *            The result to deliver.
 */
public class RequestCallbackRetry<R> {

    Logger logger = Logger.getLogger(RequestCallbackRetry.class.getName());

    final private int MAXIMUM_RETRIES_ALLOWED = 5;

    final private Action<R> action;

    final private AsyncCallback<R> asyncCallback;

    private int currentRetryCounter = 0;

    /** time to wait for reconnect upon failure */
    private int gracePeriod = 1000;

    private String fullServerWithPrefix;

    String errorMessage = "Oops. Error. Please reload. Contact support if error persists. Thanks :) ";

    public RequestCallbackRetry(String fullServerWithPrefix, Action<R> action,
            AsyncCallback<R> asyncCallback) {

        this.fullServerWithPrefix = fullServerWithPrefix;
        this.action = action;
        this.asyncCallback = asyncCallback;
    }

    public void runIt() {

        RequestBuilder builder = new RequestBuilder(action.getMethodToCall(),
                fullServerWithPrefix + action.getServiceUrl());

        RequestCallback requestCallback = new RequestCallback() {

            @Override
            public void onResponseReceived(Request request, Response response) {

                //
                if (response.getStatusCode() == Response.SC_UNAUTHORIZED) {

                    Window.Location.replace("login.html");
                } else if (response.getStatusCode() != Response.SC_OK) {

                    handleErrorGracefully();

                } else {

                    R result = action.convertFromResponseToResult(response
                            .getText());

                    asyncCallback.onSuccess(result);
                }

            }

            @Override
            public void onError(Request request, Throwable exception) {

                logger.severe("on error... unknown staus code");

                handleErrorGracefully();
            }
        };

        try {

            builder.sendRequest(action.getBodyToSend(), requestCallback);

        } catch (RequestException e) {
            e.printStackTrace();
        }

    }

    public void handleErrorGracefully() {

        // error handling...:
        if (currentRetryCounter < MAXIMUM_RETRIES_ALLOWED) {

            logger.severe("error handling in progress...");

            currentRetryCounter++;

            Timer t = new Timer() {

                public void run() {
                    runIt();

                }
            };

            t.schedule(gracePeriod);

            gracePeriod = gracePeriod * 2;

        }

        else {
            // Supersevere error.
            // reload app or redirect.
            // ===> this breaks the app but that's by intention.
            if (Window.confirm(errorMessage)) {

                Window.Location.reload();
            }
        }

    }

}
